Skip to content

Conversation

@bbopen
Copy link
Owner

@bbopen bbopen commented Jan 21, 2026

Summary

Implements #154 - Protocol version validation in the SafeCodec layer.

  • Adds validateProtocolVersion() function that checks the protocol field in responses
  • Accepts missing protocol field for backwards compatibility with older Python bridges
  • Rejects mismatched protocol versions with clear error messages
  • Uses PROTOCOL_ID constant (tywrap/1) from transport.ts
  • Includes 5 unit tests for protocol validation scenarios
  • Enables the wrong_protocol_bridge.py adversarial test

Changes

File Change
src/runtime/safe-codec.ts Added validateProtocolVersion() function and calls in decode methods
test/safe-codec.test.ts Added 5 protocol validation tests
test/adversarial_playground.test.ts Removed skip from wrong_protocol_bridge.py test

Test plan

  • All 69 safe-codec tests pass
  • wrong_protocol_bridge.py adversarial test enabled and passing
  • Protocol validation occurs before error extraction
  • Backwards compatibility maintained (missing protocol field accepted)

Note: This PR is based on feat/adr-002-bridge-protocol-migration (PR #153) since it depends on SafeCodec. Should be merged after #153.

Fixes #154

🤖 Generated with Claude Code

@coderabbitai
Copy link

coderabbitai bot commented Jan 21, 2026

Warning

Rate limit exceeded

@bbopen has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 14 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3fbde99 and 924cfc7.

📒 Files selected for processing (2)
  • src/runtime/safe-codec.ts
  • test/safe-codec.test.ts

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

This PR adds backward-compatible protocol version validation to SafeCodec. A validateProtocolVersion helper checks protocol fields in decoded responses and throws BridgeProtocolError on version mismatches. The implementation enforces validation in decodeResponse and decodeResponseAsync without requiring the protocol field to exist. A previously skipped adversarial test is re-enabled, and comprehensive test coverage is added.

Changes

Cohort / File(s) Summary
SafeCodec Protocol Validation
src/runtime/safe-codec.ts
Imports PROTOCOL_ID from transport.js; introduces validateProtocolVersion(value) helper to enforce protocol version matching; calls validation in both decodeResponse and decodeResponseAsync after JSON parsing; adds ProtocolResultResponse shape with optional protocol field; maintains backward compatibility by allowing missing protocol field.
Adversarial Tests
test/adversarial_playground.test.ts
Re-enables previously skipped wrong_protocol_bridge.py protocol-violation test; updates comment to reflect protocol validation is now implemented in SafeCodec; expects /Invalid protocol/ error pattern.
SafeCodec Unit Tests
test/safe-codec.test.ts
Adds new "decodeResponse - Protocol Validation" test suite covering backward compatibility (no protocol field), correct protocol version acceptance, wrong protocol rejection, unknown protocol rejection, and protocol validation before error extraction in error payloads.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

enhancement, area:codec

Poem

🐰 A protocol check hops into place,
Validating versions with grace,
Wrong bridges now stumble and fail,
While good ones tell their true tale—
Security strengthened, one byte at a time!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately and concisely describes the main change: adding protocol version validation to SafeCodec, which is the primary objective of this pull request.
Description check ✅ Passed The PR description is well-structured and directly related to the changeset, clearly explaining the implementation of protocol version validation with affected files and test results.
Linked Issues check ✅ Passed The code changes fully satisfy all acceptance criteria from issue #154: validates the protocol field in SafeCodec.decodeResponse(), throws BridgeProtocolError on mismatch, allows missing protocol field for backwards compatibility, includes unit tests, and enables the adversarial test.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #154 objectives: the protocol validation logic in safe-codec.ts, the comprehensive test suite additions, and the re-enabled adversarial test are all within scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f2bd079fa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@bbopen bbopen changed the base branch from feat/adr-002-bridge-protocol-migration to main January 21, 2026 17:21
Implements #154 - Protocol version validation

Changes:
- Add validateProtocolVersion() function to safe-codec.ts
- Validate protocol field in decodeResponse and decodeResponseAsync
- Accept missing protocol field for backwards compatibility
- Reject mismatched protocol versions with clear error messages
- Add 5 unit tests for protocol validation
- Enable wrong_protocol_bridge.py adversarial test

The validation uses PROTOCOL_ID constant ('tywrap/1') from transport.ts
and throws BridgeProtocolError on version mismatch.

Fixes #154

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@bbopen bbopen force-pushed the feat/protocol-version-validation branch from 9f2bd07 to 3fbde99 Compare January 21, 2026 17:24
@coderabbitai coderabbitai bot added area:codec Area: codecs and serialization enhancement New feature or request labels Jan 21, 2026
coderabbitai[bot]
coderabbitai bot previously requested changes Jan 21, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/safe-codec.test.ts`:
- Around line 689-730: Add a test to ensure SafeCodec.decodeResponse allows user
result objects that themselves contain a "protocol" field: in the existing
"decodeResponse - Protocol Validation" suite add an it() that builds payload =
JSON.stringify({ id: 1, protocol: 'tywrap/1', result: { protocol: 'http', url:
'example.com' } }), calls codec.decodeResponse<{ protocol: string; url: string
}>(payload) and expects the returned value toEqual { protocol: 'http', url:
'example.com' } (and not throw); this verifies decodeResponse handles envelope
protocol validation without rejecting user data that has a protocol property.
♻️ Duplicate comments (1)
src/runtime/safe-codec.ts (1)

165-180: Restrict protocol validation to protocol envelopes only.

This validation runs on any object with a protocol field, which is a behavioral regression. A legitimate user result like { "protocol": "http", "url": "..." } returned directly (not wrapped in an envelope) will incorrectly throw BridgeProtocolError.

The validation should only apply when the payload is a protocol envelope (i.e., has id with result or error).

🐛 Proposed fix
 function validateProtocolVersion(value: unknown): void {
   if (value === null || typeof value !== 'object') {
     return;
   }
   const obj = value as Record<string, unknown>;
+  // Only validate protocol on actual protocol envelopes (must have 'id' field)
+  if (!('id' in obj && typeof obj.id === 'number')) {
+    return;
+  }
   if ('protocol' in obj && obj.protocol !== PROTOCOL_ID) {
     throw new BridgeProtocolError(
       `Invalid protocol version: expected "${PROTOCOL_ID}", got "${obj.protocol}"`
     );
   }
 }
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5405343 and 3fbde99.

📒 Files selected for processing (3)
  • src/runtime/safe-codec.ts
  • test/adversarial_playground.test.ts
  • test/safe-codec.test.ts
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: src/runtime/bridge-core.ts:260-263
Timestamp: 2026-01-19T21:14:40.872Z
Learning: In `src/runtime/bridge-core.ts` and similar hot request/response loop implementations in the tywrap repository, avoid adding extra defensive validation (e.g., runtime shape checks on error payloads) in tight loops unless the protocol boundary is untrusted or there's a concrete bug report. The Python bridge protocol is controlled and validated via tests, so defensive checks would add unnecessary branching overhead without meaningful benefit.
Learnt from: bbopen
Repo: bbopen/tywrap PR: 152
File: docs/adr/002-bridge-protocol.md:168-172
Timestamp: 2026-01-20T16:00:49.738Z
Learning: In the tywrap project's BridgeProtocol SafeCodec implementation, Arrow format decoders can produce NaN/Infinity values from binary representations even when the raw JSON payload doesn't contain them. This is why validation for special floats must occur both before encoding (to reject invalid inputs) and after applying decoders (to catch values introduced during Arrow deserialization), protecting downstream consumers from unexpected NaN/Infinity values.
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: src/runtime/bridge-core.ts:260-298
Timestamp: 2026-01-19T21:48:45.693Z
Learning: In `src/runtime/bridge-core.ts`, keep `normalizeErrorPayload` to validate error payloads from the Python subprocess. The subprocess boundary is effectively untrusted, and normalizing error responses prevents `undefined: undefined` errors on malformed payloads. Error responses are not the hot path, so the small validation overhead is acceptable for the added resilience.
Learnt from: bbopen
Repo: bbopen/tywrap PR: 152
File: docs/adr/002-bridge-protocol.md:203-211
Timestamp: 2026-01-20T16:00:58.747Z
Learning: In the BridgeProtocol implementation for the tywrap repository, Map and Set instances should be explicitly rejected before serialization (e.g., in `safeStringify` or at the serialization entrypoint) with a clear error message like "Bridge protocol does not support Map/Set values", rather than checking for non-string keys after the fact, since Maps and Sets cannot round-trip through JSON.stringify.
📚 Learning: 2026-01-19T21:00:30.825Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: test/adversarial_playground.test.ts:339-360
Timestamp: 2026-01-19T21:00:30.825Z
Learning: In adversarial tests for the bridge (test/adversarial_playground.test.ts), recovery assertions should be kept in the finally block adjacent to cleanup to verify resilience under stress, even if that means post-timeout checks might mask the original error. The goal is to surface recovery failures as legitimate test failures.

Applied to files:

  • test/adversarial_playground.test.ts
  • test/safe-codec.test.ts
📚 Learning: 2026-01-19T21:48:27.823Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: test/runtime_bridge_fixtures.test.ts:59-66
Timestamp: 2026-01-19T21:48:27.823Z
Learning: In fixture-based tests (e.g., test/runtime_bridge_fixtures.test.ts) and similar tests in the tywrap repository, prefer early returns when Python or fixture files are unavailable. Do not rely on Vitest dynamic skip APIs; a silent pass is intentional for environments lacking Python/fixtures. Treat missing fixtures as optional soft-guards and ensure the test remains non-disruptive in non-availability scenarios.

Applied to files:

  • test/adversarial_playground.test.ts
  • test/safe-codec.test.ts
📚 Learning: 2026-01-19T21:14:40.872Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: src/runtime/bridge-core.ts:260-263
Timestamp: 2026-01-19T21:14:40.872Z
Learning: In `src/runtime/bridge-core.ts` and similar hot request/response loop implementations in the tywrap repository, avoid adding extra defensive validation (e.g., runtime shape checks on error payloads) in tight loops unless the protocol boundary is untrusted or there's a concrete bug report. The Python bridge protocol is controlled and validated via tests, so defensive checks would add unnecessary branching overhead without meaningful benefit.

Applied to files:

  • test/adversarial_playground.test.ts
  • test/safe-codec.test.ts
📚 Learning: 2026-01-19T21:48:45.693Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: src/runtime/bridge-core.ts:260-298
Timestamp: 2026-01-19T21:48:45.693Z
Learning: In `src/runtime/bridge-core.ts`, keep `normalizeErrorPayload` to validate error payloads from the Python subprocess. The subprocess boundary is effectively untrusted, and normalizing error responses prevents `undefined: undefined` errors on malformed payloads. Error responses are not the hot path, so the small validation overhead is acceptable for the added resilience.

Applied to files:

  • test/adversarial_playground.test.ts
  • src/runtime/safe-codec.ts
  • test/safe-codec.test.ts
📚 Learning: 2026-01-19T21:00:52.689Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: test/fixtures/out_of_order_bridge.py:29-48
Timestamp: 2026-01-19T21:00:52.689Z
Learning: In `test/fixtures/out_of_order_bridge.py`, the fixture intentionally leaves a pending request unanswered at EOF to simulate missing/out-of-order responses and validate bridge behavior when requests never complete; this is the exact failure mode being tested and must be preserved.

Applied to files:

  • test/adversarial_playground.test.ts
📚 Learning: 2026-01-19T21:00:52.689Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: test/fixtures/out_of_order_bridge.py:29-48
Timestamp: 2026-01-19T21:00:52.689Z
Learning: Policy for adversarial test fixtures: adversarial fixtures must preserve the failure mode they exercise, even if that behavior would be "bad" in real code. When reviewing test fixtures with unusual patterns (e.g., missing error handling, intentional protocol violations, unanswered requests), ask for clarifying comments about the intent rather than suggesting normalization or fixes.

Applied to files:

  • test/adversarial_playground.test.ts
📚 Learning: 2026-01-20T16:00:49.829Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 152
File: docs/adr/002-bridge-protocol.md:203-211
Timestamp: 2026-01-20T16:00:49.829Z
Learning: In the BridgeProtocol implementation (tywrap), reject Map and Set explicitly before serialization (e.g., in safeStringify or the serialization entrypoint) with a clear error like "Bridge protocol does not support Map/Set values". Do not rely on post-hoc checks of non-string keys at the point of JSON.stringify, since Maps/Sets cannot round-trip. This should be enforced at the boundary where data is prepared for serialization to ensure deterministic errors and prevent invalid data from propagating.

Applied to files:

  • test/adversarial_playground.test.ts
  • src/runtime/safe-codec.ts
  • test/safe-codec.test.ts
📚 Learning: 2026-01-20T01:34:07.064Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 136
File: src/runtime/node.ts:444-458
Timestamp: 2026-01-20T01:34:07.064Z
Learning: When reviewing promise-based polling patterns (e.g., recursive setTimeout in waitForAvailableWorker functions), ensure that any recursive timer is tracked and cleared on timeout or promise resolution to prevent timer leaks. Do not rely on no-op checks after settlement; verify clearTimeout is invoked in all paths and consider using an explicit cancellation (e.g., AbortController) or a guard to cancel pending timers when the promise settles.

Applied to files:

  • test/adversarial_playground.test.ts
  • src/runtime/safe-codec.ts
  • test/safe-codec.test.ts
📚 Learning: 2026-01-20T18:37:05.670Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 152
File: src/runtime/process-io.ts:37-40
Timestamp: 2026-01-20T18:37:05.670Z
Learning: In the tywrap repo, ESLint is used for linting (not Biome). Do not flag regex literals that contain control characters (e.g., \u001b, \u0000-\u001F) as lint errors, since the current ESLint configuration allows them. When reviewing TypeScript files (e.g., src/**/*.ts), rely on ESLint results and avoid raising issues about these specific control-character regex literals unless there is a competing config change or a policy requiring explicit escaping.

Applied to files:

  • test/adversarial_playground.test.ts
  • src/runtime/safe-codec.ts
  • test/safe-codec.test.ts
📚 Learning: 2026-01-19T21:14:35.390Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: src/runtime/bridge-core.ts:260-263
Timestamp: 2026-01-19T21:14:35.390Z
Learning: In src/runtime/bridge-core.ts and similar hot request/response loop implementations in the tywrap repository, avoid adding extra defensive validation (such as runtime, shape, or error-payload checks) inside tight loops unless the protocol boundary is untrusted or there is a concrete bug report. The Python bridge protocol is controlled via tests, so these checks add unnecessary branching overhead without meaningful benefit. Apply this guidance to other hot-path runtime loops in src/runtime/**/*.ts, and re-enable additional validations only when a documented risk or failure scenario is identified. Ensure tests cover protocol validation where applicable.

Applied to files:

  • src/runtime/safe-codec.ts
📚 Learning: 2026-01-20T16:00:49.738Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 152
File: docs/adr/002-bridge-protocol.md:168-172
Timestamp: 2026-01-20T16:00:49.738Z
Learning: In the tywrap project's BridgeProtocol SafeCodec implementation, Arrow format decoders can produce NaN/Infinity values from binary representations even when the raw JSON payload doesn't contain them. This is why validation for special floats must occur both before encoding (to reject invalid inputs) and after applying decoders (to catch values introduced during Arrow deserialization), protecting downstream consumers from unexpected NaN/Infinity values.

Applied to files:

  • src/runtime/safe-codec.ts
  • test/safe-codec.test.ts
📚 Learning: 2026-01-19T21:14:29.869Z
Learnt from: bbopen
Repo: bbopen/tywrap PR: 127
File: src/runtime/bridge-core.ts:375-385
Timestamp: 2026-01-19T21:14:29.869Z
Learning: In the runtime env-var parsing (e.g., in src/runtime/bridge-core.ts and similar modules), adopt a tolerant, best-effort policy: numeric env values should parse by taking the leading numeric portion (e.g., TYWRAP_CODEC_MAX_BYTES=1024abc -> 1024). Only reject clearly invalid values (non-numeric start or <= 0). This reduces surprising failures from minor typos. Add tests to cover partial numeric prefixes, and ensure downstream logic documents the trusted range; consider a small helper to extract a positive integer from a string with a safe fallback.

Applied to files:

  • src/runtime/safe-codec.ts
🧬 Code graph analysis (1)
src/runtime/safe-codec.ts (1)
src/runtime/transport.ts (1)
  • PROTOCOL_ID (20-20)
🔇 Additional comments (5)
test/adversarial_playground.test.ts (1)

488-491: LGTM!

The test is correctly enabled now that protocol version validation is implemented in SafeCodec. The expected pattern /Invalid protocol/ aligns with the error message from validateProtocolVersion().

src/runtime/safe-codec.ts (4)

14-14: LGTM!

Import of PROTOCOL_ID from transport is appropriate for centralizing the protocol version constant.


335-336: Protocol validation placement is correct.

Validating protocol version before error extraction aligns with the PR objective that protocol mismatch should be detected before attempting to process the payload content.


388-389: Consistent with sync version.

Protocol validation in the async path mirrors the sync decodeResponse method, maintaining consistency.


151-163: LGTM!

The ProtocolResultResponse interface correctly makes protocol optional for backwards compatibility, and the type guard properly identifies protocol envelopes by checking for id and result fields.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Address review feedback: only validate protocol field when the response
looks like a protocol envelope (has 'id' field). This prevents false
positives on user data that happens to contain a 'protocol' key.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@bbopen bbopen merged commit dc506a8 into main Jan 21, 2026
18 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:codec Area: codecs and serialization enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add protocol version validation to SafeCodec

2 participants